package com.lagou.sqlSession;


import com.lagou.config.BoundSql;
import com.lagou.pojo.Configuration;
import com.lagou.pojo.MappedStatement;
import com.lagou.utils.GenericTokenParser;
import com.lagou.utils.ParameterMapping;
import com.lagou.utils.ParameterMappingTokenHandler;
import com.mchange.v2.c3p0.ComboPooledDataSource;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

/**
 * @author DingYiYuan
 * @description
 * @time 2021/3/10 16:01
 */
public class SimpleExecutor implements Executor {


    @Override
    public <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object... params) throws Exception {

        //1、获取数据库链接
        Connection connection = getConnection(configuration);

        //2、转换SQL语句，将#{}转为?
        BoundSql boundSql = getBoundSql(mappedStatement.getSql());

        //3、获取预处理对象：preparedStatement
        PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSqlText());

        //4、获取参数类型
        Class<?> parameterTypeClass = getClassType(mappedStatement.getParameterType());

        //5、设置预编译sql参数
        if(null!=params&&params.length>0){
            this.setPreparedStatement(boundSql, preparedStatement, parameterTypeClass, params[0]);
        }

        List<Object> objects = new ArrayList<>();
        if(boundSql.getSqlText().startsWith("select")){
            //6、执行sql语句
            ResultSet resultSet = preparedStatement.executeQuery();

            //7、获取返回结果类型
            Class<?> resultTypeClass = getClassType(mappedStatement.getResultType());

            //8、封装返回结果集
            while (resultSet.next()) {
                Object o = resultTypeClass.newInstance();
                //元数据
                ResultSetMetaData metaData = resultSet.getMetaData();
                for (int i = 1; i <= metaData.getColumnCount(); i++) {

                    // 字段名
                    String columnName = metaData.getColumnName(i);
                    // 字段的值
                    Object value = resultSet.getObject(columnName);

                    //使用反射或者内省，根据数据库表和实体的对应关系，完成封装
                    PropertyDescriptor propertyDescriptor = new PropertyDescriptor(columnName, resultTypeClass);
                    Method writeMethod = propertyDescriptor.getWriteMethod();
                    writeMethod.invoke(o, value);
                }
                objects.add(o);
            }
            return (List<E>)objects;
        }
        if(boundSql.getSqlText().startsWith("insert")||boundSql.getSqlText().startsWith("update")||boundSql.getSqlText().startsWith("delete")){
            //6、执行sql语句
            preparedStatement.execute();
            return (List<E>)objects;
        }
        return (List<E>)objects;
    }

    //设置预编译sql参数
    private void setPreparedStatement(BoundSql boundSql, PreparedStatement preparedStatement, Class<?> parameterTypeClass, Object param) throws NoSuchFieldException, IllegalAccessException, SQLException {
        if(boundSql.getParameterMappingList().isEmpty()){
            return;
        }
        List<ParameterMapping> parameterMappingList = boundSql.getParameterMappingList();
        for (int i = 0; i < parameterMappingList.size(); i++) {

            ParameterMapping parameterMapping = parameterMappingList.get(i);
            String content = parameterMapping.getContent();
            //反射获取类的属性字段
            Field declaredField = parameterTypeClass.getDeclaredField(content);
            //暴力访问
            declaredField.setAccessible(true);
            Object o = declaredField.get(param);

            preparedStatement.setObject(i + 1, o);

        }
    }

    //获取数据库链接
    private Connection getConnection(Configuration configuration) throws ClassNotFoundException, SQLException {
        ComboPooledDataSource comboPooledDataSource = (ComboPooledDataSource) configuration.getDataSource();
        String driverClass = comboPooledDataSource.getDriverClass();
        String jdbcUrl = comboPooledDataSource.getJdbcUrl();
        String user = comboPooledDataSource.getUser();
        String password = comboPooledDataSource.getPassword();
        Class.forName(driverClass);
        return DriverManager.getConnection(jdbcUrl, user, password);
    }

    //获取参数类型
    private Class<?> getClassType(String type) throws ClassNotFoundException {
        if (type != null) {
            Class<?> aClass = Class.forName(type);
            return aClass;
        }
        return null;

    }


    //解析SQL语句，将#{}转换为？，并将#{}中的参数名称按顺序存储
    private BoundSql getBoundSql(String sql) {
        //标记处理类：配置标记解析器来完成对占位符的解析处理工作
        ParameterMappingTokenHandler parameterMappingTokenHandler = new ParameterMappingTokenHandler();
        GenericTokenParser genericTokenParser = new GenericTokenParser("#{", "}", parameterMappingTokenHandler);
        //解析出来的sql
        String parseSql = genericTokenParser.parse(sql);
        //#{}里面解析出来的参数名称
        List<ParameterMapping> parameterMappings = parameterMappingTokenHandler.getParameterMappings();

        BoundSql boundSql = new BoundSql(parseSql, parameterMappings);
        return boundSql;

    }


}
